package conf

import (
	"context"
	"database/sql"
	"fmt"
	"sync"
	"time"
	_ "github.com/go-sql-driver/mysql"
)

//全局config实例对象
//也就是我们程序，在内存中的配置对象
//程序内部获取配置，都通过读取该对象
//该config对象 什么时候被初始化？
// 配置加载的时候LoadConfigFromToml，LoadConfigFromEnv

var config *Config
var db *sql.DB
//要想获取配置，单独提供函数
//全局Config获取函数
func C() *Config{
	return config
}

//初始化一个有默认值的Config对象
func NewDefaultConfig() *Config {
	return &Config{
		App:   NewDefaultApp(),
		Log:   NewDefaultLog(),
		MySQL: NewDefaultMysql(),
	}
}

type Config struct {
	App   *App   `toml:"app"`
	Log   *Log   `toml:"log"`
	MySQL *MySQL `toml:"mysql"`
}

func NewDefaultApp() *App {
	return &App{
		Name : "demo" ,
		Host : "127.0.0.1",
		Port : "8050",
	}
}



type App struct {
	Name      string `toml:"name" env:"APP_NAME"`
	Host      string `toml:"host" env:"APP_HOST"`
	Port      string `toml:"port" env:"APP_PORT"`
}


func NewDefaultLog() *Log {
	return &Log{
		Level:"info",
		Format:TextFormat,
		To: ToStdout,
	}
}
// Log todo
//用于配置全局log对象
type Log struct {
	Level   string    `toml:"level" env:"LOG_LEVEL"`
	PathDir string    `toml:"path_dir" env:"LOG_PATH_DIR"`
	Format  LogFormat `toml:"format" env:"LOG_FORMAT"`
	To      LogTo     `toml:"to" env:"LOG_TO"`
}

func NewDefaultMysql() *MySQL {
	return &MySQL{
		Host:  "127.0.0.1",
		Port:  "3306",
		UserName: "demo",
		Password: "123456",
		Database: "demo",
		MaxOpenConn: 200,
		MaxIdleConn: 100,    
	}
}

// MySQL todo
type MySQL struct {
	Host        string `toml:"host" env:"D_MYSQL_HOST"`
	Port        string `toml:"port" env:"D_MYSQL_PORT"`
	UserName    string `toml:"username" env:"D_MYSQL_USERNAME"`
	Password    string `toml:"password" env:"D_MYSQL_PASSWORD"`
	Database    string `toml:"database" env:"D_MYSQL_DATABASE"`
	//因为使用的mysql连接池，需要池做一些规划配置
	//控制当前程序的MYSQL打开的连接数
	MaxOpenConn int    `toml:"max_open_conn" env:"D_MYSQL_MAX_OPEN_CONN"`
	//控制mysql复用，比如5，最多5个来复用
	MaxIdleConn int    `toml:"max_idle_conn" env:"D_MYSQL_MAX_IDLE_CONN"`
	//一个连接的生命周期，这个和Mysql，Server的配置有关系，必须小于Server配置
	MaxLifeTime int    `toml:"max_life_time" env:"D_MYSQL_MAX_LIFE_TIME"`
	MaxIdleTime int    `toml:"max_idle_time" env:"D_MYSQL_MAX_idle_TIME"`
	lock sync.Mutex
}



// getDBConn use to get db connection pool
//连接池，driverConn具体的连接对象,他维护者一个socket
//pool []*driverConn,维护pool里面的连接都是可用的，定期检查我们的conn健康情况
//某一个driverconn已经失效，driverConn.Reset(),清空该结构体的数据，Reconn获取一个连接，让改conn借壳存活，从而避免drivberConn结构体的内存申请的开销成本
func (m *MySQL) getDBConn() (*sql.DB, error) {
	var err error
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&multiStatements=true", m.UserName, m.Password, m.Host, m.Port, m.Database)
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		return nil, fmt.Errorf("connect to mysql<%s> error, %s", dsn, err.Error())
	}
	db.SetMaxOpenConns(m.MaxOpenConn)
	db.SetMaxIdleConns(m.MaxIdleConn)
	db.SetConnMaxLifetime(time.Second * time.Duration(m.MaxLifeTime))
	db.SetConnMaxIdleTime(time.Second * time.Duration(m.MaxIdleTime))
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := db.PingContext(ctx); err != nil {
		return nil, fmt.Errorf("ping mysql<%s> error, %s", dsn, err.Error())
	}
	return db, nil
}
//1.第一种通过loadgloabal加载实例
//2.第二种，惰性加载
func (m *MySQL) GetDB() (*sql.DB, error) {
	// 加载全局数据量单例
	m.lock.Lock()
	defer m.lock.Unlock()
	if db == nil {
		conn, err := m.getDBConn()
		if err != nil {
			return nil, err
		}
		db = conn
	}
	return db, nil
}
